package com.binance.rabbitmq.config;

import com.binance.rabbitmq.exception.RabbitmqConfigurationException;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Singular;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.AbstractExchange;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;

import java.util.Map;

/**
 * Created by robin.wu on 2018/11/29.
 */
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Log4j2
@ConfigurationProperties
public class ExchangeConfig extends AbstractConfig{

    private String name;

    private ExchangeTypes type;

    private Boolean durable;

    private Boolean autoDelete;

    private Boolean internal;

    private Boolean delayed;

    private Boolean alternateEnabled;

    @Singular
    private Map<String, Object> arguments;

    public boolean validate() {
        if (StringUtils.isEmpty(getName())) {
            log.error("Invalid Exchange Configuration : Name must be provided for an exchange");
            return false;
        }
        log.info("Exchange configuration validated successfully for exchange '{}'", getName());
        return true;
    }

    public ExchangeConfig applyDefaultConfig(ExchangeConfig defaultExchangeConfig) {
        log.debug("Applying DefaultExchangeConfig on the current ExchangeConfig :: ExchangeConfig = {{}} , DefaultExchangeConfig = {{}}", this, defaultExchangeConfig);
        setType(getDefaultConfig(getName(), "type", getType(), defaultExchangeConfig.getType(), ExchangeTypes.TOPIC));
        setDurable(getDefaultConfig(getName(), "durable", getDurable(), defaultExchangeConfig.getDurable(), Boolean.FALSE));
        setAutoDelete(getDefaultConfig(getName(), "autoDelete", getAutoDelete(), defaultExchangeConfig.getAutoDelete(), Boolean.FALSE));
        setInternal(getDefaultConfig(getName(), "internal", getInternal(), defaultExchangeConfig.getInternal(), Boolean.FALSE));
        setDelayed(getDefaultConfig(getName(), "delayed", getDelayed(), defaultExchangeConfig.getDelayed(), Boolean.FALSE));
        setAlternateEnabled(getDefaultConfig(getName(),"alternateEnabled",getAlternateEnabled(),defaultExchangeConfig.getAlternateEnabled(),Boolean.TRUE));
        setArguments(loadArguments(getArguments(), defaultExchangeConfig.getArguments()));
        setDefaultConfigApplied(true);
        log.info("DefaultExchangeConfig applied on the current ExchangeConfig :: ExchangeConfig = {{}} , DefaultExchangeConfig = {{}}", this, defaultExchangeConfig);
        return this;
    }

    public AbstractExchange buildExchange(DefaultConfig defaultConfig) {
        ExchangeConfig defaultExchangeConfig = defaultConfig.getDefaultExchange();
        AlternateConfig alternateConfig = defaultConfig.getAlternateConfig();

        if (!isDefaultConfigApplied()) {
            applyDefaultConfig(defaultExchangeConfig);
        }
        AbstractExchange exchange = new CustomExchange(getName(), getType().getValue(), getDurable(), getAutoDelete(), getArguments());

        if(Boolean.TRUE.equals(getAlternateEnabled())) {
            if(alternateConfig == null || alternateConfig.getAlternateExchange() == null || alternateConfig.getAlternateQueue() == null) {
                throw new RabbitmqConfigurationException(
                        String.format("Invalid configuration %s : ExchangeConfig/AlternateExchange must be provided when alternateEnabled=true for exchange %s.",
                                getName(), getName()));
            }
            exchange.getArguments().put("alternate-exchange",alternateConfig.getAlternateExchange().getName());
        }

        /**
         * 以下两属性用来控制延迟队列，需要rabbit plugin 支持，暂时预留
         * 本starter中提供的延迟队列是 TTL Message + DeadLetter Exchange实现的
         */
        exchange.setInternal(getInternal());
        exchange.setDelayed(getDelayed());
        return exchange;
    }
}
